﻿using Sys.Core.Models.Dto;
using Sys.Core.Models.VO;
using Sys.Core.Providers;
using MiniFox.Components;
using MiniFox.Data.EntityFramework;
using MiniFox.Platform.Applications.Models;

namespace Sys.Data
{
    [AutoResolve]
    public class MenuProvider : EntityDataProvider<PlatformDbContext>, IMenuProvider
    {
        private MenuVo MappingToVo(SysMenu sysMenu)
        {
            if (sysMenu == null) return null;
            return new MenuVo
            {
                ApplicationID = sysMenu.AppId,
                MenuID = sysMenu.MenuId,
                MenuName = sysMenu.MenuName,
                ParentMenuID = sysMenu.ParentId,
                MenuPath = sysMenu.MenuPath,
                Label = sysMenu.Label,
                Icon = sysMenu.Icon,
                Ordinal = sysMenu.Ordinal ?? 0
            };
        }
        public IEnumerable<MenuVo> GetMenuItems()
        {
            var query = (from m in this.CurrentDbContext.SysMenu
                         select new MenuVo
                         {
                             ApplicationID = m.AppId,
                             MenuID = m.MenuId,
                             MenuName = m.MenuName,
                             ParentMenuID = m.ParentId,
                             MenuPath = m.MenuPath,
                             Label = m.Label,
                             Icon = m.Icon,
                             Ordinal = m.Ordinal ?? 0
                         });

            return query.ToArray();
        }

        public bool MenuExists(string menuPath)
        {
            return this.CurrentDbContext.SysMenu.Any(m => m.MenuPath == menuPath);
        }

        public bool CreateMenuItem(MenuDto menuItem)
        {
            var sysMenu = new SysMenu();
            sysMenu.MenuId = menuItem.MenuID;
            sysMenu.AppId = menuItem.ApplicationID;
            sysMenu.ParentId = menuItem.ParentMenuID;
            sysMenu.MenuName = menuItem.MenuName;
            sysMenu.MenuPath = menuItem.MenuPath;
            sysMenu.Icon = menuItem.Icon;
            sysMenu.Label = menuItem.Label;
            sysMenu.Ordinal = menuItem.Ordinal;
            return this.Insert(sysMenu) > 0;
        }

        public bool UpdateMenuItem(MenuDto menuItem)
        {
            var sysMenu = this.CurrentDbContext.SysMenu.SingleOrDefault(m => m.MenuId == menuItem.MenuID && m.AppId == menuItem.ApplicationID);
            var prefix = $"{sysMenu.MenuPath}/";
            sysMenu.MenuName = menuItem.MenuName;
            sysMenu.MenuPath = menuItem.MenuPath;
            sysMenu.Icon = menuItem.Icon;
            sysMenu.Label = menuItem.Label;
            sysMenu.Ordinal = menuItem.Ordinal;
            List<SysMenu> sysMenus = new List<SysMenu>
            {
                sysMenu
            };

            var children = this.CurrentDbContext.SysMenu.Where(d => d.MenuPath.StartsWith(prefix)).ToArray();
            foreach (var childMenu in children)
            {
                childMenu.MenuPath = childMenu.MenuPath.Replace(prefix, $"{sysMenu.MenuPath}/");
                sysMenus.Add(childMenu);
            }

            return this.Update(sysMenus.ToArray()) > 0;
        }

        public bool DeleteMenuItem(string appId, string menuId)
        {
            var sysMenu = this.CurrentDbContext.SysMenu.SingleOrDefault(m => m.MenuId == menuId && m.AppId == appId);
            return this.DeleteMenu(sysMenu);
        }

        public bool DeleteMenuItem(string menuPath)
        {
            var sysMenu = this.CurrentDbContext.SysMenu.SingleOrDefault(m => m.MenuPath == menuPath);
            return this.DeleteMenu(sysMenu);
        }

        private bool DeleteMenu(SysMenu sysMenu)
        {
            List<object> list = new List<object>();
            var subMenus = this.CurrentDbContext.SysMenu.Where(m => m.MenuPath.StartsWith($"{sysMenu.MenuPath}/")).ToArray();
            //var menuModules = (from m in this.CurrentDbContext.SysMenu 
            //                   join mm in this.CurrentDbContext.SysMenuModule on new {m.MenuId, m.AppId} equals new { mm.MenuId, mm.AppId} 
            //                   where (m.MenuPath == sysMenu.MenuPath) || (m.MenuPath.StartsWith($"{sysMenu.MenuPath}/"))
            //                   select mm).ToArray();
            //var menuLinks = (from l in this.CurrentDbContext.SysMenuLink
            //                 join m in this.CurrentDbContext.SysMenu on new { l.MenuId, l.AppId } equals new { m.MenuId, m.AppId }
            //                 where (m.MenuPath == sysMenu.MenuPath) || (m.MenuPath.StartsWith($"{sysMenu.MenuPath}/"))
            //                 select l).ToArray();
            //list.AddRange(menuLinks);
            //list.AddRange(menuModules);
            list.AddRange(subMenus);
            list.Add(sysMenu);
            return this.Delete(list.ToArray()) > 0;
        }

        public MenuVo GetMenuItem(string appId, string menuId)
        {
            var sysMenu = this.CurrentDbContext.SysMenu.SingleOrDefault(m => m.MenuId == menuId && m.AppId == appId);
            return MappingToVo(sysMenu);
        }

        public MenuVo GetMenuItem(string menuPath)
        {
            var sysMenu = this.CurrentDbContext.SysMenu.SingleOrDefault(m => m.MenuPath == menuPath);
            return MappingToVo(sysMenu);
        }

        #region menu module

        public IEnumerable<MenuModuleVo> GetMenuModules(string appId, string menuId)
        {
            return (from m in this.CurrentDbContext.SysModule
                    join a in this.CurrentDbContext.SysApp on m.AppId equals a.AppId
                    join mm in this.CurrentDbContext.SysMenuModule on m.ModuleId equals mm.ModuleId
                    where mm.AppId == appId && mm.MenuId == menuId
                    orderby mm.Ordinal
                    select new MenuModuleVo
                    {
                        ModuleID = m.ModuleId,
                        ModuleName = m.ModuleName,
                        IsPublic = m.IsPublic,
                        ApplicationID = m.AppId,
                        Uri = m.ModuleUri,
                        QueryString = m.QueryString,
                        CreateTime = m.CreateTime,
                        UpdateTime = m.UpdateTime,
                        Enabled = m.Enabled,
                        Icon = m.Icon,
                        OpenMode = Enum.Parse<OpenMode>(m.OpenMode),
                        RequireSSL = a.Ssl,
                        ApplicationDomain = a.Host,
                        PortNumber = a.Port,
                        ApplicationPath = a.Path,
                        Label = mm.Label,
                        Ordinal = mm.Ordinal
                    }).ToArray();
        }
        public void CreateMenuModules(string appId, string menuId, string[] modules)
        {
            List<SysMenuModule> list = new List<SysMenuModule>();
            int i = 0;
            foreach(var moduleId in modules)
            {
                var m = new SysMenuModule();
                m.AppId = appId;
                m.MenuId = menuId;
                m.ModuleId = moduleId;
                m.Ordinal = ++i;

                list.Add(m);
            }
            this.Insert(list.ToArray());
        }
        public void UpdateMenuModule(MenuModuleDto menuModule)
        {
            var m = this.CurrentDbContext.SysMenuModule.SingleOrDefault(t => t.AppId == menuModule.ApplicationID && t.MenuId == menuModule.MenuID && t.ModuleId == menuModule.ModuleID);
            m.Label  = menuModule.Label;
            m.Ordinal = menuModule.Ordinal;

            this.Update(m);
        }

        public void DeleteMenuModules(string appId, string menuId, string[] modules)
        {
            var array = (from mm in this.CurrentDbContext.SysMenuModule where mm.AppId == appId && mm.MenuId == menuId && modules.Contains(mm.ModuleId) select mm).ToArray();
            this.Delete(array);
        }


        #endregion

        #region menu link

        public IEnumerable<MenuLinkVo> GetMenuLinks(string appId, string menuId)
        {
            var query = (from l in this.CurrentDbContext.SysMenuLink
                         where l.AppId == appId && l.MenuId == menuId
                         orderby l.Ordinal
                         select new MenuLinkVo
                         {
                             LinkID = l.LinkId,
                             LinkName = l.LinkName,
                             LinkUri = l.LinkUri,
                             OpenMode = Enum.Parse<OpenMode>(l.OpenMode),
                             Icon = l.Icon,
                             Label = l.Label
                         });
            return query.ToArray();
        }

        public MenuLinkVo GetMenuLink(string linkID)
        {
            var query = (from l in this.CurrentDbContext.SysMenuLink
                         orderby l.Ordinal
                         select new MenuLinkVo
                         {
                             LinkID = l.LinkId,
                             LinkName = l.LinkName,
                             LinkUri = l.LinkUri,
                             OpenMode = Enum.Parse<OpenMode>(l.OpenMode),
                             Icon = l.Icon,
                             Label = l.Label
                         });
            return query.SingleOrDefault(l => l.LinkID == linkID);
        }
        public bool CreateMenuLink(MenuLinkDto link)
        {
            var menuLink = new SysMenuLink();
            menuLink.LinkId = link.LinkID;
            menuLink.AppId = link.ApplicationID;
            menuLink.MenuId = link.MenuID;
            menuLink.LinkName = link.LinkName;
            menuLink.Label = link.Label;
            menuLink.LinkUri = link.LinkUri;
            menuLink.Icon = link.Icon;
            menuLink.OpenMode = link.OpenMode.ToString();
            menuLink.Ordinal = link.Ordinal;
            return this.Insert(menuLink) > 0;
        }

        public bool DeleteMenuLink(string linkId)
        {
            var menuLink = this.CurrentDbContext.SysMenuLink.SingleOrDefault(l => l.LinkId == linkId);
            return this.Delete(menuLink) > 0;
        }

        public bool UpdateMenuLink(MenuLinkDto link)
        {
            var menuLink = this.CurrentDbContext.SysMenuLink.SingleOrDefault(l => l.LinkId == link.LinkID);
            menuLink.AppId = link.ApplicationID;
            menuLink.MenuId = link.MenuID;
            menuLink.LinkName = link.LinkName;
            menuLink.Label = link.Label;
            menuLink.LinkUri = link.LinkUri;
            menuLink.Icon = link.Icon;
            menuLink.OpenMode = link.OpenMode.ToString();
            menuLink.Ordinal = link.Ordinal;
            return this.Update(menuLink) > 0;
        }
        #endregion
    }
}
